package com.mmc.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mmc.entity.Menu;
import com.mmc.mapper.MenuMapper;
import com.mmc.mapper.RoleMenuMapper;
import com.mmc.service.MenuService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 菜单服务实现类
 *
 * @author kunfe
 * @since 2024-01-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    private final MenuMapper menuMapper;
    private final RoleMenuMapper roleMenuMapper;

    @Override
    public List<Menu> getMenuTree() {
        List<Menu> allMenus = list(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getStatus, 1)
                .orderByAsc(Menu::getSort));
        
        return buildTree(allMenus);
    }

    @Override
    public List<Menu> getMenuTreeByUserId(Long userId) {
        List<Menu> userMenus = menuMapper.selectMenusByUserId(userId);
        return buildTree(userMenus);
    }

    @Override
    public List<Menu> getAllMenus(String menuName, Integer status) {
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<Menu>()
                .like(ObjectUtil.isNotEmpty(menuName), Menu::getMenuName, menuName)
                .eq(ObjectUtil.isNotNull(status), Menu::getStatus, status)
                .orderByAsc(Menu::getSort);
        
        return list(wrapper);
    }

    @Override
    public List<Menu> getMenusByParentId(Long parentId) {
        return list(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getParentId, parentId)
                .eq(Menu::getStatus, 1)
                .orderByAsc(Menu::getSort));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createMenu(Menu menu) {
        // 设置默认值
        if (menu.getVisible() == null) {
            menu.setVisible(1);
        }
        if (menu.getStatus() == null) {
            menu.setStatus(1);
        }
        if (menu.getIsCache() == null) {
            menu.setIsCache(0);
        }
        if (menu.getIsFrame() == null) {
            menu.setIsFrame(0);
        }
        if (menu.getSort() == null) {
            menu.setSort(0);
        }
        if (menu.getParentId() == null) {
            menu.setParentId(0L);
        }
        
        return save(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateMenu(Menu menu) {
        Menu existMenu = getById(menu.getId());
        if (existMenu == null) {
            throw new RuntimeException("菜单不存在");
        }
        
        // 不允许将父菜单设置为自己或自己的子菜单
        if (menu.getParentId() != null && !menu.getParentId().equals(0L)) {
            if (menu.getParentId().equals(menu.getId())) {
                throw new RuntimeException("不能将父菜单设置为自己");
            }
            
            List<Long> childIds = getChildMenuIds(menu.getId());
            if (childIds.contains(menu.getParentId())) {
                throw new RuntimeException("不能将父菜单设置为自己的子菜单");
            }
        }
        
        return updateById(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteMenu(Long menuId) {
        // 检查是否有子菜单
        if (hasChildren(menuId)) {
            throw new RuntimeException("该菜单下还有子菜单，无法删除");
        }
        
        // 检查是否被角色使用
        if (hasRoles(menuId)) {
            throw new RuntimeException("该菜单已分配给角色，无法删除");
        }
        
        // 删除角色菜单关联
        roleMenuMapper.deleteByMenuId(menuId);
        
        return removeById(menuId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteMenu(List<Long> menuIds) {
        if (CollUtil.isEmpty(menuIds)) {
            return false;
        }
        
        // 检查是否有子菜单或被角色使用
        for (Long menuId : menuIds) {
            if (hasChildren(menuId)) {
                Menu menu = getById(menuId);
                throw new RuntimeException("菜单【" + menu.getMenuName() + "】下还有子菜单，无法删除");
            }
            
            if (hasRoles(menuId)) {
                Menu menu = getById(menuId);
                throw new RuntimeException("菜单【" + menu.getMenuName() + "】已分配给角色，无法删除");
            }
        }
        
        // 删除角色菜单关联
        for (Long menuId : menuIds) {
            roleMenuMapper.deleteByMenuId(menuId);
        }
        
        return removeByIds(menuIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeStatus(Long menuId, Integer status) {
        Menu menu = new Menu();
        menu.setId(menuId);
        menu.setStatus(status);
        
        return updateById(menu);
    }

    @Override
    public boolean hasChildren(Long menuId) {
        return count(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getParentId, menuId)) > 0;
    }

    @Override
    public boolean hasRoles(Long menuId) {
        return menuMapper.countRolesByMenuId(menuId) > 0;
    }

    @Override
    public Menu getMenuByPermission(String permission) {
        return getOne(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getPermission, permission));
    }

    @Override
    public List<Menu> buildRoutes(List<Menu> menus) {
        return buildTree(menus);
    }

    /**
     * 构建菜单树
     */
    private List<Menu> buildTree(List<Menu> menus) {
        if (CollUtil.isEmpty(menus)) {
            return new ArrayList<>();
        }
        
        // 获取顶级菜单
        List<Menu> rootMenus = menus.stream()
                .filter(menu -> menu.getParentId() == null || menu.getParentId().equals(0L))
                .collect(Collectors.toList());
        
        // 递归构建子菜单
        for (Menu rootMenu : rootMenus) {
            rootMenu.setChildren(getChildren(rootMenu.getId(), menus));
        }
        
        return rootMenus;
    }

    /**
     * 递归获取子菜单
     */
    private List<Menu> getChildren(Long parentId, List<Menu> allMenus) {
        List<Menu> children = allMenus.stream()
                .filter(menu -> parentId.equals(menu.getParentId()))
                .collect(Collectors.toList());
        
        for (Menu child : children) {
            child.setChildren(getChildren(child.getId(), allMenus));
        }
        
        return children;
    }

    /**
     * 获取所有子菜单ID
     */
    private List<Long> getChildMenuIds(Long parentId) {
        List<Long> childIds = new ArrayList<>();
        List<Menu> children = list(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getParentId, parentId));
        
        for (Menu child : children) {
            childIds.add(child.getId());
            childIds.addAll(getChildMenuIds(child.getId()));
        }
        
        return childIds;
    }
} 